-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Make Bind() ViewModel parameters nullable. #2468
Conversation
ViewModel objects are passed in mainly to help enforce generic typing without the user needing to manually type in all the generics themselves. Being passed to this internal functionality as object seems unnecessary
The parameter is mainly there to simplify generic specification for the calling user. Therefore, it doesn't matter if it is null. Making the parameter nullable means it lines up cleaner with ReactiveControl's ViewModel member, which is 99% of the time what is being passed in.
Sorry, didn't mean to close the PR. Will try out and let you know what I find. |
@@ -17,7 +17,7 @@ namespace ReactiveUI.Winforms | |||
public class ContentControlBindingHook : IPropertyBindingHook | |||
{ | |||
/// <inheritdoc/> | |||
public bool ExecuteHook(object source, object target, Func<IObservedChange<object, object>[]> getCurrentViewModelProperties, Func<IObservedChange<object, object>[]> getCurrentViewProperties, BindingDirection direction) | |||
public bool ExecuteHook(object target, Func<IObservedChange<object, object>[]> getCurrentViewModelProperties, Func<IObservedChange<object, object>[]> getCurrentViewProperties, BindingDirection direction) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please remove this from this PR and make it a separate PR, doesn't seem to be related to the nullability factor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, can do. I agree it was a tangential change
Sorry about the overeager change. Reversed it, and the overall changes should be much cleaner now. Let me know if you want a flatten or anything. The preceding null check /w ArgumentNullException throw made the pass to ExecuteHook clean anyway. So definitely better to leave as is. |
It's quite common in Xamarin Forms app btw that the "ViewModel" may be null initially btw. Eg on those platforms WhenActivated isn't required always. So the |
Alright, so then the solution might be to just remove ArgumentNullException, and utilize The only potential issue I can see is that the returned ReactiveBinding will have a Same with ExecuteHook. The source parameter claims the object wont be null, but in the Xamarin path, it may be |
If you have the viewModel nullable without the ArgumentNullException, does that compile? That should allow users to keep their current syntax, or at least use ? right. |
Removing the ArgumentNullException by itself will make it not compile. This is because the ReactiveBinding we're returning wants a viewmodel that is not null.
I would vote 2, as that's giving users accurate API. The viewmodel may be null in Xamarin. So we need to allow for it to be in the API? |
Yeah, 2 is fine as long as it doesn't change existing public api (apart from nullability) |
Thanks for putting together this PR, know there are some complications from it. |
My pleasure, I don't mind putting in the work. Part of the reason for the quick PR was to get some actual code changes to look at and discuss for clarity. I expected to have to make some adjustments on the base. Pushed up a new commit with the ArgumentNullExceptions removed, and the appropriate members from ReactiveBinding and IPropertyBindingHook made nullable. Seems clean enough |
Will do some tests but this looks good. Thanks |
Are these failing tests something I need to address? Or are they part of the approval process with the different technologies? |
If you have a windows machine run the tests and change the .received extension to .approved on the API approval tests. |
Attempted to do this one for you but didn't have permissions. Here's a patch file for you. |
Thanks for the diff patch! I would've been quite lost looking around for those on my lonesome, haha. |
Is there a particular reason this wasn't done for command bindings as well (i.e. |
Hi @gtbuchanan . There's a related issue open #2486, and the last comment left by @glennawatson mentioned he was working on something. Maybe he can give an update. |
@gtbuchanan I didn't avoid them for any specific reason. Just an oversight as I don't typically use those calls. If they seem like a good candidate for the same refactor I'd say go for it |
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
What kind of change does this PR introduce?
This PR modifies the
PropertyBindingMixins
used mainly withinReactiveUserControl
codebehindWhenActivated
blocks.It changes the
viewModel
members to be nullable.What is the current behavior?
Bit of contextual overview:
The ViewModel parameter in
Bind
is mostly unused. The reason it exists is to simplify generic specification of the call. By passing the ViewModel in, the generics can be implied, whereas if the parameter was omitted, the user would have to type out the verbose generic args themselves for every call. Otherwise, the parameter is mostly unneeded, and theoretically could be removed.99% of the time
Bind
is used,ReactiveUserControl.ViewModel
member is what is passed in. The current misalignment in nullability, though, means the user has to pass in the ViewModel with the override operator:this.Bind(this.ViewModel!, ...
What is the new behavior?
Since the ViewModel is unused, and just there for typing, it doesn't actually matter if the parameter nullable or not.
So why make it nullable?
By shifting the parameter to be nullable, it aligns with the
ReactiveUserControl.ViewModel
member, meaning the call is cleaner for the end user:this.Bind(this.ViewModel, ...
This is much cleaner for the user, and is less confusing, as requiring a
!
operator makes the user question if they're doing something wrong, and/or tempts them to need to check thatViewModel
is not null first.. which we know is an unnecessary step.This API shift takes the burden/questions off the user on if they're doing something wrong, and lets them use
Bind
more as they would expect.What might this PR break?
I added ArgumentNullExceptions to check that the viewmodel is not null. We know in typical usage it will not be null, as WhenActivated will never be called with null ViewModels, which is where Bind is being invoked from. Still, it is good to check that it is not null internally anyway, as it definitely means something is in an error state, as this should never happen.
One reason to do so, is that the ViewModel parameter IS used in one spot: It's given to the ReactiveBinding class that is returned, which has a ViewModel member, which is not nullable. It is good to check that the VM is not null, just as it's an error state if that was to ever happen, and it means we safely fill this member with a viewmodel object that is not null, as it expects.
One note is that the current methodology of passing in
this.ViewModel!
is no safer, we're still potentially "lying" and setting a null view model object to ReactiveBinding's non-null member. The ArgumentNullException is actually a bit safer on that front, nipping an odd situation in the bud early.Summary
I think this change gives the user an experience they're expecting, while retaining the safety of the system overall.
I had some remaining errors related to some missing SDKs for some of the more obscure platform targets. I think everything is okay. We'll let the build system double check, though.